package tsif.tcluster.netty.impl;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;

import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * netty编码工具
 * 
 */
public class NettyCodecUtils {
	public static final int MSG_HEAD_SIZE = 2; // head len
	public static final int MODE_GZIP = 0x01;

	/** 解码处理接口 **/
	public interface IDecode<T> {
		public T read(DataInput inBuf) throws IOException;
	}

	/** 编码处理接口 **/
	public interface IEncode<T> {
		public void write(T data, DataOutput outBuf) throws IOException;
	}

	/**
	 * 编码数据(支持gzip)
	 * 
	 * @param mode
	 *            模式, 0x1为使用gzip
	 * **/
	public static <T> void encode(ByteBuf buffer, T data, int mode, IEncode<T> encode) throws Exception {
		// 申请数据流
		buffer.writeShort(0);
		buffer.writeByte(mode); // 写入模式

		// 转成输出流写入
		OutputStream outputStream = new ByteBufOutputStream(buffer);
		DataOutput outBuf = (DataOutput) outputStream;

		try {
			// 启动gzip
			if ((mode & MODE_GZIP) > 0) {
				outputStream = new DataOutputStream(new GZIPOutputStream(outputStream));
				outBuf = (DataOutput) outputStream;
			}
			// write
			encode.write(data, outBuf);
		} finally {
			if (outputStream != null) {
				outputStream.flush();
				outputStream.close();
				outputStream = null;
			}
		}

		// 输出二进制数据
		// Log.info("buffer1:" + toString(buffer));

		// int dlen = (obj.getData() != null) ? obj.getData().length : 0;
		// Log.info("write:" + length + " " + dlen + " " + Arrays.toString(obj.getData()));

		// 写入长度
		int nowLength = buffer.readableBytes();
		buffer.writerIndex(0);
		buffer.writeShort(nowLength);
		buffer.writerIndex(nowLength);

		// 输出二进制数据
		// Log.info("buffer2:" + toString(buffer));
	}

	/** 解码处理 **/
	public static <T> T decode(ByteBuf in, IDecode<T> decode) throws IOException {
		// 消息头
		int nowLength = in.readableBytes();
		if (nowLength < MSG_HEAD_SIZE) {
			return null;
		}

		// 解密头
		ByteBuf buffer = in.slice(); // 切割一份(内存一样, 只是起始点不同)
		int length = buffer.readShort(); // 读取消息长度
		if (length <= 0) {
			throw new RuntimeException("消息长度错误: length=" + length);
		}
		// 检测数据包大小
		if (nowLength < length) {
			return null; // 数据尚未足够, 等下次啦.
		}
		// 读取数据模式
		byte mode = buffer.readByte();

		// 转成数据流处理
		InputStream inputStream = new ByteBufInputStream(buffer);
		DataInput inBuf = (DataInput) inputStream;

		try {
			// gzip 检测
			if ((mode & MODE_GZIP) > 0) {
				inputStream = new DataInputStream(new GZIPInputStream(inputStream));
				inBuf = (DataInput) inputStream;
			}

			// read msg
			return decode.read(inBuf);

		} finally {
			if (inputStream != null) {
				inputStream.close();
				inputStream = null;
			}
			// 数据提取标记
			in.readerIndex(in.readerIndex() + length); // 标记读取位置
		}
	}

	/** 复制数据(不影响ByteBuf的内容) **/
	public static byte[] copyByte(ByteBuf byteBuf) {
		byte[] temps = new byte[byteBuf.capacity()];
		byteBuf.readBytes(temps);
		byteBuf.readerIndex(0);
		return temps;
		// Log.info("buffer1:" + temps.length + " " + Arrays.toString(temps));
	}

	/** 输出数据流的内容 **/
	public static String toString(ByteBuf byteBuf) {
		return Arrays.toString(copyByte(byteBuf));
	}
}
